package net.takela.common.security.filter;

import net.takela.common.security.SecurityProperties;
import org.springframework.security.access.AccessDeniedException;
import org.springframework.security.access.intercept.InterceptorStatusToken;
import org.springframework.security.web.FilterInvocation;
import org.springframework.security.web.access.intercept.FilterSecurityInterceptor;

import jakarta.servlet.ServletException;
import org.springframework.util.AntPathMatcher;

import java.io.IOException;

public class AuthFilterSecurityInterceptor extends FilterSecurityInterceptor {
    private static final String FILTER_APPLIED = "__spring_security_custom_filterSecurityInterceptor_filterApplied";

    private SecurityProperties securityProperties;
    private AntPathMatcher antPathMatcher = new AntPathMatcher();
    /**
     * 
     */
    public AuthFilterSecurityInterceptor( SecurityProperties securityProperties){
        this.securityProperties = securityProperties;
    }
    /**
     * 
     */
    public void invoke(FilterInvocation filterInvocation) throws IOException, ServletException {
        boolean isAnonymous = securityProperties.getAnonymousUrls().stream().anyMatch( url -> antPathMatcher.match(url, filterInvocation.getRequest().getRequestURI()) );
        if (isAnonymous){
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
            return;
        }
        if (isApplied(filterInvocation) && this.isObserveOncePerRequest()) {
            // filter already applied to this request and user wants us to observe
            // once-per-request handling, so don't re-do security checking
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
            return;
        }
        // first time this request being called, so perform security checking
        if (filterInvocation.getRequest() != null && this.isObserveOncePerRequest()) {
            filterInvocation.getRequest().setAttribute(FILTER_APPLIED, Boolean.TRUE);
        }
        InterceptorStatusToken token = super.beforeInvocation(filterInvocation);
        if (token == null){
            throw new AccessDeniedException(
                    this.messages.getMessage("AuthFilterSecurityInterceptor.accessDenied", "Not found permission"));
        }
        try {
            filterInvocation.getChain().doFilter(filterInvocation.getRequest(), filterInvocation.getResponse());
        }
        finally {
            super.finallyInvocation(token);
        }
        super.afterInvocation(token, null);
    }

    private boolean isApplied(FilterInvocation filterInvocation) {
        return (filterInvocation.getRequest() != null)
                && (filterInvocation.getRequest().getAttribute(FILTER_APPLIED) != null);
    }
}
